package com.company.hrmadmin.modules.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.company.hrmadmin.common.constant.ErrorEnum;
import com.company.hrmadmin.common.constant.GlobalConstant;
import com.company.hrmadmin.common.exception.BusinessException;
import com.company.hrmadmin.common.exception.GlobalException;
import com.company.hrmadmin.common.util.*;
import com.company.hrmadmin.modules.domain.LoginAuthor;
import com.company.hrmadmin.modules.domain.LoginToken;
import com.company.hrmadmin.modules.domain.Salary;
import com.company.hrmadmin.modules.domain.User;
import com.company.hrmadmin.modules.entity.dto.login.ResetPasswordDTO;
import com.company.hrmadmin.modules.entity.dto.profile.UpdateAddressDTO;
import com.company.hrmadmin.modules.entity.dto.profile.UpdateCollegeDTO;
import com.company.hrmadmin.modules.entity.dto.profile.UpdatePasswordDTO;
import com.company.hrmadmin.modules.entity.vo.IndexPanelVO;
import com.company.hrmadmin.modules.entity.vo.UserProfileVO;
import com.company.hrmadmin.modules.service.*;
import com.company.hrmadmin.modules.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * @author Xmirror
 * @description 针对表【bs_user(员工)】的数据库操作Service实现
 * @createDate 2022-03-18 15:38:50
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Value("${hrm.path.upload}")
    private String uploadPath;
    @Value("${hrm.path.domain}")
    private String domain;
    @Value("${server.servlet.context-path}")
    private String contextPath;
    @Resource
    private UserMapper userMapper;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private SalaryService salaryService;
    @Autowired
    private CompanyService companyService;
    @Autowired
    private MailClient mailClient;
    @Autowired
    private TemplateEngine templateEngine;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private LoginTokenService loginTokenService;
    @Autowired
    private LoginAuthorService loginAuthorService;

    @Override
    public void getAvatar(String fileName, HttpServletResponse response) {
        fileName = uploadPath + "/" + fileName;
        // 将图片使用输出流写入 response 对象
        try (
                OutputStream outputStream = response.getOutputStream();
                FileInputStream fileInputStream = new FileInputStream(fileName)
        ) {
            byte[] buffer = new byte[1024];
            int b;
            while ((b = fileInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, b);
            }
        } catch (IOException e) {
            log.error("读取用户头像失败：" + e.getMessage());
        }
    }

    @Override
    public String uploadAvatar(MultipartFile avatarImage, String token) {
        if (avatarImage == null) {
            throw new BusinessException(ErrorEnum.AVATAR_FILE_NULL_ERROR);
        }
        String filename = avatarImage.getOriginalFilename();
        String filetype = "";
        // 判断文件类型
        if (!StringUtils.isBlank(filename)) {
            filetype = filename.substring(filename.lastIndexOf("."));
            // 文件格式正确性判断
            if (!GlobalConstant.AVATAR_FILE_TYPE.contains(filetype)) {
                throw new BusinessException(ErrorEnum.UPLOAD_AVATAR_FILE_NOT_CORRECT);
            }
        }
        filename = RandomUtil.generateSimpleUUID() + filetype;
        File file = new File(uploadPath + "/" + filename);
        // 将文件存储到目标文件夹中
        try {
            avatarImage.transferTo(file);
        } catch (IOException e) {
            log.error("头像图片存储失败：" + e.getMessage());
            throw new BusinessException(ErrorEnum.AVATAR_UPLOAD_ERROR);
        }
        // 更新之后的路径应该为 web 访问路径，如 http://localhost:9000/hrm/user/avatar/xxx.jpg
        String headerUrl = domain + contextPath + "/user/avatar/" + filename;
        LoginToken loginToken = loginTokenService.getLoginToken(token);
        User user = userMapper.selectById(loginToken.getUserId());
        Optional.ofNullable(user).orElseThrow(() -> new GlobalException(ErrorEnum.USER_ID_NULL_ERROR));
        user.setAvatarUrl(headerUrl);
        return userMapper.updateById(user) >= 1 ? headerUrl : null;
    }

    @Override
    public UserProfileVO getUserProfile(Integer userId) {
        UserProfileVO vo = new UserProfileVO();
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(ErrorEnum.USER_NOT_EXIST);
        }
        vo.setEmail(user.getEmail());
        vo.setPhone(user.getPhone());
        vo.setPost(user.getPostName());
        vo.setAvatar(user.getAvatarUrl());
        vo.setName(user.getName());
        vo.setAddress(user.getAddress());
        vo.setWorkId(user.getWorkId());
        vo.setCollege(user.getCollege());
        vo.setRole(userRoleService.getRoleName(userId));
        vo.setHireTime(user.getHireTime());
        vo.setQuitTime(user.getQuitTime());
        return vo;
    }

    @Override
    public Integer setUserCollege(UpdateCollegeDTO dto) {
        User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getWorkId, dto.getWorkId()));
        if (user == null) {
            throw new GlobalException(ErrorEnum.USER_NOT_EXIST);
        }
        // 修改学院信息
        if (dto.getNewCollege() == null || dto.getNewCollege().length() == 0 ||
                dto.getNewCollege().replaceAll(" +", "").equals("")) {
            throw new BusinessException(ErrorEnum.INPUT_NULL_ERROR);
        }
        user.setCollege(dto.getNewCollege());
        return userMapper.update(user, Wrappers.lambdaUpdate(User.class)
                .eq(User::getWorkId, dto.getWorkId()));
    }

    @Override
    public Integer setUserAddress(UpdateAddressDTO dto) {
        User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
                .eq(User::getWorkId, dto.getWorkId()));
        if (user == null) {
            throw new GlobalException(ErrorEnum.USER_NOT_EXIST);
        }
        // 修改地址信息
        if (dto.getNewAddress() == null || dto.getNewAddress().length() == 0 ||
                dto.getNewAddress().replaceAll(" +", "").equals("")) {
            throw new BusinessException(ErrorEnum.INPUT_NULL_ERROR);
        }
        user.setAddress(dto.getNewAddress());
        return userMapper.update(user, Wrappers.lambdaUpdate(User.class)
                .eq(User::getWorkId, dto.getWorkId()));
    }

    @Override
    public IndexPanelVO getIndexPanel(Integer userId) {
        User user = userMapper.selectById(userId);
        IndexPanelVO vo = new IndexPanelVO();
        if (user == null) {
            throw new GlobalException(ErrorEnum.USER_NOT_EXIST);
        }
        Integer companyId = user.getCompanyId();
        Integer depId = user.getDepartmentId();
        Long depCount = userMapper.selectCount(Wrappers.lambdaQuery(User.class)
                .eq(User::getCompanyId, companyId)
                .eq(User::getDepartmentId, depId));
        vo.setDepartment(Math.toIntExact(depCount));
        // 计算当前用户薪水情况
        Integer integer = salaryService.calSalaryByUserId(userId);
        if (integer == 0) {
            throw new GlobalException(ErrorEnum.SALARY_INFO_NOT_EXIST);
        }
        Salary salary = salaryService.getSalaryByUserId(userId);
        if (salary != null && salary.getPayableSalary() != null) {
            vo.setSalary(salary.getPayableSalary().doubleValue());
        }
        Integer totalPeople = companyService.getTotalPeople(userId);
        vo.setTotalPeople(Optional.ofNullable(totalPeople).orElse(0));
        // TODO: 消息模块暂时搁置
        vo.setMessage(null);
        return vo;
    }

    @Override
    public Integer sendVerify(String email) {
        // 发送验证码邮件
        Context context = new Context();
        context.setVariable("email", email);
        String code = RandomUtil.randomNumber(GlobalConstant.VERIFY_CODE_LENGTH);
        context.setVariable("verify", code);
        // 模板引擎调用网页，将其中的数据填充之后，生成一个 HTML 网页字符串对象，格式化网页
        String process = templateEngine.process("/mail/forget", context);
        mailClient.sendMail(email, "企业账户-忘记密码", process);
        // 将验证码保存在 redis 中
        redisUtil.setCacheObject(RedisKeyUtil.getVerifyKey(email), code, GlobalConstant.VERIFY_AVAILABLE_TIME, TimeUnit.SECONDS);
        return 1;
    }

    @Override
    public Integer resetPassword(ResetPasswordDTO dto) {
        if (dto.getPassword() == null || dto.getConfirmPassword() == null ||
                dto.getAccount() == null || dto.getVerify() == null) {
            throw new BusinessException(ErrorEnum.INPUT_NULL_ERROR);
        }
        String pattern = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
        boolean matches = Pattern.matches(pattern, dto.getAccount());
        if (!matches) {
            throw new BusinessException(ErrorEnum.ACCOUNT_SHOULD_BE_EMAIL);
        }
        Object verifyObj = redisUtil.getCacheObject(RedisKeyUtil.getVerifyKey(dto.getAccount()));
        Optional.ofNullable(verifyObj).orElseThrow(() -> new GlobalException(ErrorEnum.VERIFY_NOT_EXIST));
        String verify = verifyObj.toString();
        if (!dto.getPassword().equals(dto.getConfirmPassword())) {
            throw new BusinessException(ErrorEnum.PASSWORD_NOT_CONSISTENT);
        }
        if (!dto.getVerify().equals(verify)) {
            throw new BusinessException(ErrorEnum.VERIFY_INCORRECT);
        }
        LoginAuthor loginAuthor = loginAuthorService.getByAccount(dto.getAccount());
        Optional.ofNullable(loginAuthor).orElseThrow(() -> new BusinessException(ErrorEnum.EMAIL_NOT_EXIST));
        UpdatePasswordDTO updatePasswordDTO = new UpdatePasswordDTO(loginAuthor.getUserId(),
                loginAuthor.getPassword(), dto.getPassword(), dto.getConfirmPassword());
        return loginAuthorService.updatePassword(updatePasswordDTO);
    }

}




